package org.sdnstproject.interlayer.openflowfuzzing;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.sdnstproject.interlayer.interlayerProxy.InterlayerProxy;
import org.sdnstproject.interlayer.interlayerProxy.MsgHander;
import org.sdnstproject.interlayer.interlayerProxy.SwitchToInterlayer;
import org.sdnstproject.interlayer.utils.SSHShell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.JSchException;

public class OpenFLowFuzzer {
	private static final Logger log = LoggerFactory.getLogger(OpenFLowFuzzer.class);
	public ArrayList<String> strateges;
	private int interlayerPort;
	private String controllerIP;
	private int controllerPort;
	public ThreadPoolExecutor pool;
	public MsgHander msgHander;
	public volatile List<SwitchToInterlayer> switchThreads;
	public BlockingQueue<byte[]> msgQueue;
	public SSHShell ssh;
	public ArrayList<String> strategies;
	public Configuration config = Configuration.getInstance(); 
	
	public OpenFLowFuzzer() {
		this.interlayerPort = config.getInterlayerPort();
        this.controllerIP = config.getControllerIP();
        this.controllerPort = config.getControllerPort();
        this.pool = new ThreadPoolExecutor(2*config.getSwitchNumber()+6, 4*config.getSwitchNumber()+6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));
	}
	
	public OpenFLowFuzzer(ThreadPoolExecutor pool) {
		this.interlayerPort = config.getInterlayerPort();
        this.controllerIP = config.getControllerIP();
        this.controllerPort = config.getControllerPort();
        this.pool = pool;
    }
	
	public OpenFLowFuzzer(String ip, int interlayerPort, int controllerPort) {
        this.interlayerPort = interlayerPort;
        this.controllerIP = ip;
        this.controllerPort = controllerPort;
        this.pool = new ThreadPoolExecutor(2*config.getSwitchNumber()+6, 4*config.getSwitchNumber()+6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));
    }
	
	public void replay(String stra) {
		Strategy strategy = StrategesGen.getStrategyObject(stra);
		doFuzzing(strategy);
	}
	
	public void Fuzz() {
		String ret = null;
		FileWriter fileWriter =null;
		try {
			fileWriter = new FileWriter("./log/result.log",true);
			StrategesGen.readFromFile(strategies, "./src/main/java/strategy.log");
			for(String s:strateges) {
				Strategy strategy = StrategesGen.getStrategyObject(s);
				if((ret = doFuzzing(strategy)) != null) {
					fileWriter.write("["+ret+"]"+s);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void doFuzz() throws Exception {
		InterlayerProxy interlayerProxy = new InterlayerProxy(controllerIP, interlayerPort, controllerPort, pool,null);
		//先启动控制器
		runController();
		//再启动中间层
		pool.execute(interlayerProxy);
		Thread.sleep(500);
		//最后启动mininet
		runMininet();
		Thread.sleep(500);
	}
	public String doFuzzing(Strategy strategy) {
		try {
			InterlayerProxy interlayerProxy = new InterlayerProxy(controllerIP, interlayerPort, controllerPort, pool, strategy);
			//先启动控制器
			runController();
			//再启动中间层
			pool.execute(interlayerProxy);
			Thread.sleep(500);
			//最后启动mininet
			runMininet();
			Thread.sleep(500);
			String result = "";
//			if(ret.contains("DOWN")) {
//				result = getResult();
//			}
//			//先停止mininet
//			stopMininet();
//			//再停止中间层
//			interlayerProxy.Stop();
//			//最后停止控制器
//			stopController();
			return result;
		}catch (Exception e) {
//			stopController();
//			stopMininet();
//			e.printStackTrace();
			return "";
		}
	}

	public String getResult() {

		String result = ""; 
		return result;
	}

	public void stopMininet() {
		String command = "sudo mn -c";
		runCommand(command);
	}

	public void stopController() {
		String command = "";
		switch (config.getTargetController()) {
			case "Ryu":
				command = "pkill ryu-manager";
				break;
			case "ryu":
				command = "pkill ryu-manager";
				break;
			case "floodlight":
				command = "pkill java";
				break;
		}
		runCommand(command);
	}

	public void runController() {
		String command = "";
		switch (config.getTargetController()) {
			case "Ryu":
//exec ryu-manager --observe-links ryu.app.simple_switch_13 ryu.app.rest_topology ryu.app.ofctl_rest ryu.app.ws_topology ryu.app.gui_topology.gui_topology >> /dev/null 2>&1 &
				command = "ryu-manager --observe-links ryu.app.simple_switch_13"+
						"ryu.app.rest_topology ryu.app.ofctl_rest ryu.app.ws_topology"+
						" ryu.app.gui_topology.gui_topology";
				break;
			case "floodlight":
				command = "cd floodlight && java -jar target/floodlight.jar";
				break;
		}
		runCommand(command);
	}

	public void runMininet() {
		String command = "sudo mn --controller=remote,ip="+config.getLocalIP()+",port="+config.getInterlayerPort();
		runCommand(command);
	}
	
	public void runCommand(String command) {
		SSHShell shell = new SSHShell(config.getControllerUser(), config.getControllerIP(), config.getControllerPasswd(),command);
		try {
			shell.connect();
			pool.execute(shell);
		} catch (JSchException e) {
			e.printStackTrace();
		}
	}
	

}
